package name.cyrax.red.jpv.impl.funcs;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.funcs.BinaryFunctor;
import name.cyrax.red.jpv.api.funcs.BoundUnaryFunctor;
import name.cyrax.red.jpv.api.funcs.FunctorInstance;
import name.cyrax.red.jpv.api.funcs.UnaryFunctor;
import name.cyrax.red.jpv.api.funcs.info.FunctorMethod;
import name.cyrax.red.jpv.api.funcs.info.ValueType;
import name.cyrax.red.jpv.impl.funcs.core.FunctorBinaryImpl;
import name.cyrax.red.jpv.impl.funcs.core.FunctorUnaryDoubleImpl;
import name.cyrax.red.jpv.impl.funcs.core.FunctorUnaryImpl;
import name.cyrax.red.jpv.impl.funcs.core.FunctorUnaryIntegerImpl;
import name.cyrax.red.jpv.impl.funcs.core.FunctorUnaryLongImpl;

public final class Functions
{
	private Functions() {};

	private static final BinaryFunctor funcAdd = new FunctorBinaryImpl() {
		@Override public int opInteger(int v0, int v1) {
			return v0 + v1;
		}	
		@Override public long opLong(long v0, long v1) {
			return v0 + v1;
		}	
		@Override public double opDouble(double v0, double v1) {
			return v0 + v1;
		}
		@Override public String getName()
		{
			return "add";
		}
	};
	public static BinaryFunctor add() { return funcAdd;	}

	private static final BinaryFunctor funcSubt = new FunctorBinaryImpl() {
		@Override public int opInteger(int v0, int v1) {
			return v0 - v1;
		}	
		@Override public long opLong(long v0, long v1) {
			return v0 - v1;
		}	
		@Override public double opDouble(double v0, double v1) {
			return v0 - v1;
		}
		@Override public String getName() {
			return "subt";
		}
	};
	public static BinaryFunctor subt() { return funcSubt;	}

	private static final UnaryFunctor funcNegate = new FunctorUnaryImpl() {
		@Override public String getName() {
			return "negate";
		}
		@Override public int opInteger(int v0) {
			return -v0;
		}
		@Override public long opLong(long v0) {
			return -v0;
		}
		@Override public double opDouble(double v0) {
			return -v0;
		}
	};
	public static UnaryFunctor negate() 
	{ 
		return funcNegate; 
	}
	
	public static UnaryFunctor scaleAndCast(final double scaleBy) 
	{ 
		return new FunctorUnaryImpl() {

			@Override public String getName()
			{
				return "scale_by " + scaleBy;
			}

			@Override public int opInteger(int v0) {
				return (int) (v0 * scaleBy);
			}

			@Override public long opLong(long v0) {
				return (long) (v0 * scaleBy);
			}

			@Override public double opDouble(double v0) {
				return v0 * scaleBy;
			}
		};
	}

	public static BoundUnaryFunctor<Double> scale(final double scaleBy) 
	{ 
		return new FunctorUnaryDoubleImpl() {

			@Override public String getName()
			{
				return "scale_by " + scaleBy;
			}

			@Override public double opDouble(int v0)	{
				return v0 * scaleBy;
			}

			@Override public double opDouble(long v0) {
				return v0 * scaleBy;
			}

			@Override public double opDouble(double v0) {
				return v0 * scaleBy;
			}
		};
	}

//		public static UnaryFunctor scale(final int mulBy, final int divBy) 
//		{ 
//			return new UnaryFunctorImpl() {
//
//				@Override protected int op(int v0)
//				{
//					return (int) op((long) v0);
//				}
//
//				@Override protected long op(long v0)
//				{
//					return v0 * mulBy / divBy;
//				}
//
//				@Override protected double op(double v0)
//				{
//					return v0 * mulBy / divBy;
//				}
//			};
//		}
//
	private static final BoundUnaryFunctor<Integer> castInt = new FunctorUnaryIntegerImpl() {
		@Override public String getName() {
			return "castToInt";
		}			
		@Override public int opInteger(double v0) {
			return (int) v0;
		}
		@Override public int opInteger(long v0) {
			return (int) v0;
		}
		@Override public int opInteger(int v0) {
			return v0;
		}
		@Override protected FunctorInstance getFunctorInstance(FunctorMethod functorMethod) {
			return super.getFunctorInstance(FunctorMethod.newCast(ValueType.INTEGER, ValueType.UNKNOWN));
		};
		@Override public IntegerAdapter newAdapter(final IntegerAdapter p0) {
			return p0;
		}
	};

	public static BoundUnaryFunctor<Integer> castInteger() 
	{ 
		return castInt; 
	}

	private static final BoundUnaryFunctor<Long> castLong = new FunctorUnaryLongImpl() {
		@Override public String getName() {
			return "castToLong";
		}			
		@Override public long opLong(double v0) {
			return (long) v0;
		}
		@Override public long opLong(long v0) {
			return v0;
		}
		@Override public long opLong(int v0) {
			return v0;
		}
		@Override protected FunctorInstance getFunctorInstance(FunctorMethod functorMethod) {
			return super.getFunctorInstance(FunctorMethod.newCast(ValueType.LONG, ValueType.UNKNOWN));
		};
		public LongAdapter newAdapter(LongAdapter p0) {
			return p0;
		};
	};

	public static BoundUnaryFunctor<Long> castLong() 
	{
		return castLong;
	}
	
	private static final BoundUnaryFunctor<Double> castDbl = new FunctorUnaryDoubleImpl() {
		@Override public String getName() {
			return "castToDouble";
		}			
		@Override public double opDouble(double v0) {
			return v0;
		}
		@Override public double opDouble(long v0) {
			return v0;
		}
		@Override public double opDouble(int v0) {
			return v0;
		}
		@Override protected FunctorInstance getFunctorInstance(FunctorMethod functorMethod) {
			return super.getFunctorInstance(FunctorMethod.newCast(ValueType.DOUBLE, ValueType.UNKNOWN));
		};
		public DoubleAdapter newAdapter(DoubleAdapter p0) {
			return p0;
		};
	};

	public static BoundUnaryFunctor<Double> castDouble() 
	{ 
		return castDbl; 
	}
}
